package com.poizon.infrastructure.idem.core.domain.entity;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.poizon.infrastructure.idem.core.domain.value.enums.IdemReqType;
import com.poizon.infrastructure.idem.core.domain.value.exception.IdempotentDbStoreException;
import com.poizon.infrastructure.idem.core.repo.IIdemDbStoreRepo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 领域模型/幂等
 */
@Slf4j
@Data
public class IdemEntity implements Serializable {
    public static IIdemDbStoreRepo iIdemDbStoreRepo;

    /**
     * 幂等键
     */
    private String reqId;

    /**
     * 幂等请求响应
     */
    private Object resp;

    /**
     * 幂等请求来源类型
     */
    private IdemReqType reqType;

    /**
     * 幂等数据创建时间
     */
    private Date createTime;

    /**
     * 状态字段
     * 用来判断返回结果是否已经写入(主要用来兼容void)
     * @true resp已经更新到数据库
     * @false resp还没有更新到数据库
     */
    private Boolean status = false;

    /**
     * 构建/幂等插入对象
     */
    public static IdemEntity buildCreateIdemDto(@NotNull String idemKey, @NotNull IdemReqType reqType) {
        log.debug("[通用幂等框架] 开始构建幂等空对象,buildCreateIdemDto() called with parameters => 【idemKey = {}】, 【reqType = {}】", idemKey, reqType);

        IdemEntity idemEntity = new IdemEntity();
        idemEntity.setReqId(idemKey);
        idemEntity.setResp(null);
        idemEntity.setReqType(reqType);
        idemEntity.setCreateTime(new Date());
        idemEntity.setStatus(false);

        if (log.isDebugEnabled()) {
            log.debug("[通用幂等框架] 成功构建幂等空对象={}", JSON.toJSONString(idemEntity));
        }

        return idemEntity;
    }

    /**
     * 场景1= 单独线程获取
     * 场景2= 多线程同时获取
     * 场景3= 单线程获取 同时其他线程在操作其他方法
     */
    public static IdemEntity getIdemDto(@NotBlank String idemKey) {
        try {
            log.debug("[通用幂等框架] 尝试根据idemKey={},获取幂等数据", idemKey);
            IdemEntity idemEntity = iIdemDbStoreRepo.getIdemDto(idemKey);
            if (idemEntity == null) {
                log.debug("[通用幂等框架] 无法获取idemKey={}的幂等数据", idemKey);
                return null;
            }
            if (log.isDebugEnabled()) {
                log.debug("[通用幂等框架] 幂等key={},已执行幂等操作,不允许重复执行,resp={}", idemKey, JSON.toJSONString(idemEntity));
            }
            return idemEntity;
        } catch (Exception e) {
            log.error(StrUtil.format("[通用幂等框架] 根据idemKey={},获取幂等数据异常", idemKey), e);
            throw new IdempotentDbStoreException(e);
        }
    }

    /**
     * 设置幂等键
     */
    public Boolean setIdemKey(@NotBlank String idemKey, @NotNull IdemEntity emptyRespIdemEntity, @NotNull Long expireTime, @NotNull TimeUnit timeUnit) {
        try {
            Boolean success = iIdemDbStoreRepo.setIdemKey(idemKey, emptyRespIdemEntity, expireTime, timeUnit);
            if (!success) {
                log.debug("[通用幂等框架] 幂等key={},已经存在,设置失败", idemKey);
                return false;
            }
            log.debug("[通用幂等框架] 幂等key={},空对象设置成功", idemKey);
            return true;
        } catch (Exception e) {
            log.error(StrUtil.format("[通用幂等框架] 根据idemKey={},设置幂等数据异常,请检查", idemKey), e);
            throw new IdempotentDbStoreException(e);
        }
    }

    /**
     * 更新幂等响应数据
     */
    public void updateIdemResp(Object resp, @NotNull Long expireTime, @NotNull TimeUnit timeUnit, @NotBlank String idemKey) {
        this.setResp(resp);
        this.setStatus(true);

        try {
            iIdemDbStoreRepo.updateIdemResp(idemKey, this, expireTime, timeUnit);
        } catch (Exception e) {
            log.error(StrUtil.format("[通用幂等框架] 保存幂等响应失败,请人工检查,IdemEntity={}", JSON.toJSONString(this)), e);
            throw new IdempotentDbStoreException(e);
        }
    }

    /**
     * 幂等key/回退
     */
    public void delete(@NotBlank String idemKey) {
        log.debug("[通用幂等框架] 准备删除幂等key={}", idemKey);
        try {
            iIdemDbStoreRepo.delete(idemKey);
            log.debug("[通用幂等框架] 成功删除幂等key={}", idemKey);
        } catch (Exception e) {
            log.error(StrUtil.format("[通用幂等框架] 幂等key={},删除幂等数据异常", idemKey), e);
            throw new IdempotentDbStoreException(e);
        }
    }
}
